<!DOCTYPE html>
<HEAD>
  <!-- ganja -->
  <SCRIPT SRC="../ganja.js"></SCRIPT>
  <!-- ace editor -->
  <SCRIPT SRC="https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.8/ace.js"></SCRIPT>
  <!-- katex math typesetting -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/katex.min.css" integrity="sha384-9eLZqc9ds8eNjO3TmqPeYcDj8n+Qfa4nuSiGYa6DjLNcv9BtN69ZIulL9+8CqC9Y" crossorigin="anonymous">
  <script defer src="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/katex.min.js" integrity="sha384-K3vbOmF2BtaVai+Qk37uypf7VrgBubhQreNQe9aGsz9lB63dIFiQVlJbr92dw2Lx" crossorigin="anonymous"></script>
  <script defer src="https://cdn.jsdelivr.net/npm/katex@0.10.0/dist/contrib/auto-render.min.js" integrity="sha384-kmZOZB5ObwgQnS/DuDg6TScgOiWWBiVt0plIRkZCmE6rDZGrEOQeHM5PcHi+nyqe" crossorigin="anonymous" onload="renderMathInElement(document.body);"></script>
  <!-- styling -->
  <style>
    body     { margin:0; width:100%; height:100%; max-height:100%; overflow:hidden; }
    .main    { display:flex; flex-direction:column; height:100vh; width:100vw; max-height:100vh; overflow:hidden; }
    .graph   { flex-shrink:1; flex-grow:1; position:relative; }
    .console { min-height:240px; flex-shrink:0; height:200px; margin:0; background-color: #AAA; font-size:18px; overflow:auto; }
    .input   { margin : 0; flex-shrink:0; font-size: 30px; border:solid 2px gray; }
  </style>
</HEAD>
<BODY>
  <DIV CLASS="main">
    <DIV CLASS="graph"></DIV>
    <PRE CLASS="console">
GAViewer syntax test.
* mode : CGA (ala init(2))
* basis vectors : e1, e2, e3, e4, e5, ni, no, I3, I5
* supports colors, dynamic, pt
* more coming ...
* eg : 
    a = pt( e1 )
    b = pt( e2 )
    dynamic { c = a ^ b ^ ni; } // ; prevents render
    cyan(c)
    red(a ^ b ^ no)
</PRE>
    <PRE CLASS="input"></PRE>    
  </DIV>
  <SCRIPT>
  // Testing - simple 2D PGA to start.
    var A = Algebra(4,1);
    var Graph = document.querySelector(".graph").appendChild(A.graph([],{gl:true,grid:true,conformal:true,z:8}));
    Object.assign(Graph.style,{width:'100%',height:'100%',position:'absolute'});
    
  // Testing simple collect parser that displays each command ..
    var cmds = [], results = [], histp=-1;
    var out = document.querySelector(".console");
    var toTex = (x)=>{ var r=x.match(/e?\d+|\bno\b|\bni\b/)?'$'+x.replace(/\&/g,"\\vee ")
                                                   .replace(/\^/g,"\\wedge ")
                                                   .replace(/\*\*(\w+)/g,"^{$1}")
                                                   .replace(/\*\*(\(.*?\))/g,"^{$1}")
                                                   .replace(/alpha/g,'\\alpha')
                                                   .replace(/beta/g,'\\beta')
                                                   .replace(/gamma/g,'\\gamma')
                                                   .replace(/\bno\b|^no$/g,"n_o")
                                                   .replace(/\bni\b|^ni$/g,"n_\\infty")
                                                   .replace(/e_/g,'e').replace(/e(\d+)/g,"e_{$1}")+'$':x; console.log(r); return r; }
    var addCmd = (cmd)=>{ 
      // no assignment, no graphing .. just evaluate and print the output.
      histp=-1; var ocmd=cmd;
      try {
        var dynamic = cmd.match(/dynamic\s*\{/)
        if (dynamic) cmd = cmd.replace(/dynamic\s*\{/,"").replace(/\}\s*$/,"");
        var vname = cmd.match( /^\s*([A-Za-z_\-]+)\s*=(.*)/ );
        if (vname) {
          var result = A.inline(new Function("return "+(dynamic?" ()=>":"")+vname[2]))();
          window[vname[1]] = result;
        } else {
          var result = A.inline(new Function("return "+(dynamic?" ()=>":"")+cmd))();
        }
        if (result != undefined) { cmds.push(ocmd); if (result._color) results.push(result._color); results.push(result); }
      } catch (e) {
        out.innerHTML += '<FONT COLOR=#800>'+e.message+'</FONT>\n';
        out.scrollTop += 1000;
        return cmd;
      }
      if (result !== undefined) {
        while (result.call) result=result();
        out.innerHTML += '['+cmds.length+'] '+(dynamic?"dynamic{"+toTex(cmd)+"}":toTex(cmd))+'\n'+((vname&&vname[1])||"ans")+' = '+toTex(''+result)+'\n';
        renderMathInElement(out,{delimiters:[{left: '$',right:'$',display:!1}]});
        out.scrollTop += 1000;
        if (!cmd.match(/;\s*$/)) {
          Graph.value.splice(0,Graph.value.length,...results);
          Graph.update(Graph.value);
        }
      }
      return "";
    }

  // Setup ACE editor for command input. (bah - not really a readline style cli)
    var el = document.querySelector('.input')
    var editor = ace.edit(el);
    editor.setOptions({
        maxLines: 1, // make it 1 line
        autoScrollEditorIntoView: true,
        highlightActiveLine: false,
        printMargin: false,
        showGutter: false,
        mode: "ace/mode/javascript",
        theme: "ace/theme/tomorrow_night_eighties"
    });
    // remove newlines in pasted text
    editor.on("paste", function(e) {
        e.text = e.text.replace(/[\r\n]+/g, " ");
    });
    // make mouse position clipping nicer
    editor.renderer.screenToTextCoordinates = function(x, y) {
        var pos = this.pixelToScreenCoordinates(x, y);
        return this.session.screenToDocumentPosition(
            Math.min(this.session.getScreenLength() - 1, Math.max(pos.row, 0)),
            Math.max(pos.column, 0)
        );
    };
    // disable Enter Shift-Enter keys
    editor.$blockScrolling = Infinity;
    editor.commands.bindKey("Enter|Shift-Enter", function(){ 
      try {
        editor.setValue(addCmd(editor.getValue()));
        editor.clearSelection();
      } catch(e){ 
        out.innerHTML+="<FONT COLOR=#800>"+e.message+"</FONT>\n";
        out.scrollTop+=1000;
      }
    });  
    editor.commands.bindKey("Up",function () {
      if (histp==-1) histp = cmds.length;
      editor.setValue(cmds[Math.max(0,--histp)]);
    })
    editor.commands.bindKey("down",function () {
      if (histp==-1) histp = cmds.length;
      editor.setValue(cmds[Math.min(cmds.length,++histp)]||"");
    })
    editor.focus();
  </SCRIPT>
  <SCRIPT>
  // PREAMBLE - GAVIEWER commands.
    var clc     = ()=>{ cmds = []; out.innerHTML=''; histp = -1; },
        clf     = ()=>{ Graph.update([]); results = []; },
        cld     = ()=>{},
        exp     = (x)=>A.Exp(x),
        inverse = (x)=>x.Inverse,
        grade   = (a,b)=>{ while(a.call)a=a.call(); return b!==undefined?a.Grade(b):a.Grade(4).VLength?4:a.Grade(3).VLength?3:a.Grade(2).VLength?2:a.Grade(1).VLength?1:0},
        dual    = (x)=>A.Dual(x),
        norm    = (x)=>A.Length(x),
        unit    = (x)=>A.Normalize(x),
        red     = (x)=>{ x._color = 0xFF0000; return x; },
        green   = (x)=>{ x._color = 0x00FF00; return x; },
        blue    = (x)=>{ x._color = 0x0000FF; return x; },
        yellow  = (x)=>{ x._color = 0xFFFF00; return x; },
        pink    = (x)=>{ x._color = 0xFF00FF; return x; },
        cyan    = (x)=>{ x._color = 0x00FFFF; return x; },
        white   = (x)=>{ x._color = 0xFFFFFF; return x; },
        black   = (x)=>{ x._color = 0xFFFFFF; return x; },
        pt      = A.inline(x=>no+x+.5*x.Length*ni);
  // 2D PGA      
//    const [e0,e1,e2,e12,e01,e02,e012] = A.inline(()=>[1e0,1e1,1e2,1e12,1e01,1e02,1e012])();         
  // 3D CGA      
    const [e1,e2,e3,e4,e5,e12,e13,e14,e15,e23,e24,e25,e34,e35,e45,no,ni,I3,I5,pi,e] = A.inline(()=>[1e1,1e2,1e3,1e4,1e5,1e12,1e13,1e14,1e15,1e23,1e24,1e25,1e34,1e35,1e45,()=>.5e5-.5e4,()=>1e4+1e5,1e123,1e12345,Math.PI,Math.E])();         
  </SCRIPT>
</BODY>